/*  
 * Can be invoked by several threads.
 * 
 * Uses the classloader to fetch js modules.
 */
var log = java.util.logging.Logger.getLogger("initRequire")

var require = function (id) {
	
	try {

		require.lock.lock() // Avoid threading issues while loading modules.
		
		if (!require.modules[id]) {

			var modCode = require.loadMod(id)

			var exports = {
				dependencies:[]
			}
			require.modules[id] = exports
			var module = {}
			
			var abs_id = function( other_id ) {
				other_id = "" + other_id
				
				if ("./" == other_id.substring(0,2)) {
					return id.substring(0, id.lastIndexOf("/")) + other_id.substring(1)
				}
				
				if ("/" != other_id[0]) {
					return "/" + other_id
				}
				
				return other_id
			}
			
			var requireForModule = function(id) {
				id = abs_id(id)

				module.id = id
				
				var mod = require( id )
				
				var d = exports.dependencies
				if (d.indexOf(id) == -1) {
					d[d.length] = id
				}
				
				return mod
			}
			
			requireForModule.RequireError = require.RequireError
			requireForModule.reset = require.reset
			requireForModule.load = function(id) {
				return require.load( abs_id(id) )
			}
			requireForModule.loadMod = function(id) {
				return require.loadMod( abs_id(id) )
			}
			requireForModule.env = require.env
			
			var context = {
				exports: exports,
				module: module,
				require: requireForModule
			}
			
			if(!modCode) {
				log.warning("Can't load module " + id)
				throw new require.RequireError(id, "module not found")
			}

			org.mozilla.javascript.Context.getCurrentContext().evaluateString(
				context, 
				modCode,
				id,
				1,
				null
			)
		}

		return require.modules[id]
//	} catch (x) {
//		if (x instanceof require.RequireError) {
//			throw x
//		} else {
//			throw new require.RequireError(id, x)
//		}
	} finally {
		require.lock.unlock()
	}
}

require.env = "rhino"

require.modules = {}
require.lock = new java.util.concurrent.locks.ReentrantLock()

require.RequireError = function(moduleId, cause) {
	this.moduleId = moduleId
	this.cause = cause
	
}
require.RequireError.prototype = {}
require.RequireError.prototype.toString = function() {
	return "Error loading " + this.moduleId + ( this.cause ? ". Cause: " + this.cause : "" )
}
require.RequireError.prototype.name = "RequireError"

require.load = function(path) {
	if ("/" != path[0]) {
		path = "/" +  path
	}
	return java.lang.Class.forName("aure.jslib.StaticHelper").getResourceAsStream(path)
}

require.loadMod = function(id) {
	function path(id) {
		return "" + id + ".js"
	}
	
	function readFromStream(stream) {
		var io = java.io
		var reader = new io.BufferedReader( new io.InputStreamReader(stream) )
		try {
			var stringBuffer = new java.lang.StringBuffer()
			
			var line = ""
			while( line = reader.readLine()) {
				stringBuffer.append(line)
				stringBuffer.append("\n")
			}
			
			return stringBuffer.toString()
		} finally {
			reader.close()
		}
	}

	var stream = require.load(path(id))
	if (!stream) { return null }
	
	return readFromStream(stream)
}

/**
 * Resets the module cache. 
 * 
 * It will reload all the modules. Old modules referenced will still be working.
 */
require.reset = function() {
	try {
		require.lock.lock()
		require.modules = {}
	} finally {
		require.lock.unlock()
	}
	
}